package Communication;

import java.util.HashMap;
import java.util.HashSet;
import java.util.ArrayList;

import Communication.AretePheromone;
import Communication.NoeudPheromone;
import Communication.Pheromone;

/**
 * TODO description
 */
public class GraphPheromone {

	private static GraphPheromone singleton = null;
	private static int lecture, ecriture;
	private HashMap<String,NoeudPheromone> map;
	private HashMap<String, ArrayList<Pheromone>> phName;
	private HashMap<Pheromone,AretePheromone> pheromonesAr;
	
	private int nbUser;
	private int cpt;
	
	public static void init(int nbUser)
	{
		if(singleton == null)
		{
			singleton = new GraphPheromone(nbUser);
			lecture = 0;
			ecriture = 0;
		}
	}
	
	public static void clear()
	{
		singleton.lock(true);
		singleton.cpt++;
		if(singleton.cpt == 1)
		{
			singleton.map.clear();
			singleton.pheromonesAr.clear();
			singleton.phName.clear();
		}
		
		if(singleton.cpt == singleton.nbUser)
			singleton.cpt = 0;
		singleton.unlock(true);
	}
 
	private void lock(boolean write)
	{
		if(write)
		{
			synchronized(this)
			{
				while(!(lecture == 0 & ecriture == 0))
				{
					try 
					{
						this.wait();
					}
					catch(Exception e)
					{
						System.err.println(e.getMessage());
					}
				}
				ecriture++;
			}
		}
		else
		{
			synchronized(this)
			{
				while(!(ecriture == 0))
				{
					try
					{
						this.wait();
					}
					catch(Exception e)
					{
						System.err.println(e.getMessage());
					}
				}
				lecture++;
			}
		}
	}

	private void unlock(boolean write)
	{
		synchronized(this)
		{
			if(write)
				ecriture--;
			else
				lecture--;
			if(lecture+ecriture > 0 )
				this.notify();
		}
	}
	
	private GraphPheromone(int nbUser)
	{
		map = new HashMap<String,NoeudPheromone>();
		pheromonesAr = new HashMap<Pheromone,AretePheromone>();
		phName = new HashMap<String, ArrayList<Pheromone>>();
		this.nbUser = nbUser;
		this.cpt = 0;
	}
	
	public static GraphPheromone getSingleton()
	{
		return singleton;
	}
	
	public static String pathToAgent(String node, String name)
	{
		singleton.lock(false);
		NoeudPheromone n = singleton.findAgent(node, name);
		String nodeName = null;
		if(n != null)
			nodeName = n.getName();
		singleton.unlock(false);
		return nodeName;
	}
	
	private NoeudPheromone findAgent(String node, String name)
	{
		NoeudPheromone noeud = map.get(name);
		ArrayList<Pheromone> list = new ArrayList<Pheromone>();
		ArrayList<NoeudPheromone> listN = new ArrayList<NoeudPheromone>();
		
		for(AretePheromone a : noeud.getAretes().values())
		{
			for(Pheromone p : a.getPheromones())
				if(p.getName().equals(name))
				{
					list.add(p);
					listN.add(a.getArrive());
				}
		}
		
		if(list.isEmpty())
			return null;
		
		int index = 0;
		int phMax = list.get(0).getValue();
		NoeudPheromone n = listN.get(0); 
		
		for(Pheromone p : list)
		{
			if(p.getValue() > phMax)
			{
				phMax = p.getValue();
				n = listN.get(list.indexOf(p));
			}
		}
		
		return n;
	}

	public static void AddNoeudPheromone(String name)
	{
		singleton.lock(true);
		singleton.addNoeud(new NoeudPheromone(name));
		singleton.unlock(true);
	}

	private void addNoeud(NoeudPheromone n)
	{
		map.put(n.getName(),n);
	}
	
	public static void addAretePheromone(String node1, String node2)
	{
		singleton.lock(true);
		singleton.addArete(singleton.map.get(node1), singleton.map.get(node2));
		singleton.unlock(true);
	}
	
	private void addArete(NoeudPheromone n1, NoeudPheromone n2)
	{
		n1.addNoeud(n2);
	}
	
	private void putPheromone(String name, String pos, int value, HashSet<NoeudPheromone> noeuds)
	{
		NoeudPheromone n = map.get(pos);
		
		for(AretePheromone a : n.getAretes().values())
		{
			if(!noeuds.contains(a.getArrive()))
			{
				noeuds.add(a.getArrive());
				a.addPheromone(new Pheromone(name, value));
				if(value > 1)
					  putPheromone(name,a.getArrive().getName(),value-1,noeuds);
			}
			
				
		}
	}

	private void deposerPheromone(String name, String pos)
	{
		HashSet<NoeudPheromone> noeuds = new HashSet<NoeudPheromone>();
		putPheromone(name,pos,Pheromone.MAXPHEROMONE,noeuds);
	}
	
	public static void laisserPheromone(String name,String pos)
	{
		singleton.lock(true);
		singleton.deposerPheromone(name, pos);
		singleton.unlock(true);
	}
	
	public static void updatePh(String name)
	{
		singleton.lock(true);
		singleton.updatePheromones(name);
		singleton.unlock(true);
	}
	
	private void updatePheromones(String name)
	{
		if(!phName.containsKey(name))
			return;
		
		for(Pheromone ph : phName.get(name))
		{
			ph.nextStep();
			if(ph.value == 0)
			{
				pheromonesAr.get(ph).removePheromone(ph);
				pheromonesAr.remove(ph);
			}
		}
	}
	
	private boolean containsNode(String node)
	{
		return singleton.map.containsKey(node);
	}
	
	public static boolean noeudPresent(String name)
	{
		singleton.lock(false);
		boolean ret = singleton.containsNode(name);
		singleton.unlock(false);
		return ret;
	}
	
	void addPheromoneGraph(AretePheromone a, Pheromone ph)
	{
		pheromonesAr.put(ph, a);
		if(phName.containsKey(ph.getName()))
			phName.get(ph).add(ph);
		else
		{
			ArrayList<Pheromone> l = new ArrayList<Pheromone>();
			l.add(ph);
			phName.put(ph.getName(), l);
		}
	}
	
}